/*
 * framework & main part of SuperMarios.
 * Pengyu CHEN, 2011.
 * cpy.prefers.you@gmail.com
 *
 * distributed under terms of GPLv3.
 */

/* include your headers here */
#include "../include/graphics.h"
#include "../include/object.h"
#include "../include/supermario.h"
#include "../include/global_settings.h"
#include "../include/event.h"
#include "../include/crash_handler.h"
#include "../include/default_handler.h"
#include <math.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>

/* flags used in timer to avoid multi-thread problems */
Flags flags;

/* a sample timer callback function.
 * NOTICE: calling functions or writing to objects other than flags are
 * DISABLED in timer callback functions, you MUST read the flags and run your
 * code in the main loop.
 */
static Uint32 sampleTimerFunc(Uint32 interval, void *parameter)
{
	flags.timer_flag = GL_TRUE;
	return interval;
}

/* a sample main loop function */
static void sampleMainLoopFunc()
{
	/* check the flag(s), or just codes if there is no need checking flag(s).*/
	if (flags.timer_flag)
	{
		/* do your things here */
	}
	return;
}

float ELEVATION = -0.8f;
int number_of_marios = 2;

struct control
{
	unsigned int left, right, jump, quit, down, shoot;
}ctrl[5] = {{SDLK_LEFT, SDLK_RIGHT, SDLK_UP, SDLK_ESCAPE, SDLK_DOWN, SDLK_l},
				{SDLK_a, SDLK_d, SDLK_w, SDLK_ESCAPE, SDLK_s, SDLK_q}};

static void gameInit()
{
    srand((unsigned int )time(NULL));

	pObject mario[MAX_PLAYERS];
	Object bckgnd = {0, BCKGROUND, 0, 1, -1.0, -1.0, 2.0, 2.0, 0};
	Object platform1 = {HALF_SOLID, PLATFORM, 0, 1, {0.0f, -0.8f}, {0.3f, 0.3f}, 0};
	Object platform2 = {HALF_SOLID, PLATFORM, 0, 1, {0.2f, -0.8f}, {0.4f, 0.6f}, 1};
	Object platform3 = {HALF_SOLID, PLATFORM, 0, 1, {0.59f, -0.8f}, {0.41f, 0.4f}, 2};
	Object platform4 = {HALF_SOLID, PLATFORM, 0, 1, {0.28f, -0.8f}, {0.72f, 1.0f}, 3};
	Object platform5 = {HALF_SOLID, PLATFORM, 0, 1, {-0.4f, -0.1f}, {0.4f, 0.4f}, 0};
	Object platform6 = {HALF_SOLID, PLATFORM, 0, 1, {-0.5f, 0}, {0.4f, 0.5f}, 2};
	Object hardblock1 = {SOLID, HARD_BLOCK, 0, 1, {0.8f, -0.1f}, {0.1f, 0.1f}, 0};
	Object hardblock2 = {SOLID, HARD_BLOCK, 0, 1, {0.9f, -0.1f}, {0.1f, 0.1f}, 0};
	Object ground = {SOLID, HARD_BLOCK, 0, 1, {-1.0f, -1.0f}, {0.1f, 0.1f}, 1};
	Object fragileblock1 = {SOLID, FRAGILE_BLOCK, 0, 1, {-0.4f, -0.4f}, {0.1f, 0.1f}, 0};
	Object fragileblock2 = {SOLID, FRAGILE_BLOCK, 0, 1, {-0.3f, -0.4f}, {0.1f, 0.1f}, 0};
	Object fragileblock3 = {SOLID, FRAGILE_BLOCK, 0, 1, {-0.2f, -0.4f}, {0.1f, 0.1f}, 0};
	Object fragileblock4 = {SOLID, FRAGILE_BLOCK, 0, 1, {-0.1f, -0.4f}, {0.1f, 0.1f}, 0};
	Object randombox1 = {SOLID, RANDOM_BOX, 0, 1, {0.5f, 0.4f}, {0.1f, 0.1f}, 0};
	Object randombox2 = {SOLID, RANDOM_BOX, 0, 1, {-0.4f, 0.7f}, {0.1f, 0.1f}, 0};
	Object cloud1 = {SOLID, CLOUD, 0, 1, {-1.0f, 0.6f}, {0.1f, 0.1f}, 0};
	Object cloud2 = {SOLID, CLOUD, 0, 1, {-0.8f, 0.3f}, {0.1f, 0.1f}, 0};
	Object cloud3 = {SOLID, CLOUD, 0, 1, {-0.9f, 0.0f}, {0.1f, 0.1f}, 0};
	Object cloud4 = {SOLID, CLOUD, 0, 1, {-0.7f, -0.3f}, {0.1f, 0.1f}, 0};
	Object cloud5 = {SOLID, CLOUD, 0, 1, {-0.9f, -0.6f}, {0.1f, 0.1f}, 0};
	Object fragileblock5 = {SOLID, FRAGILE_BLOCK, 0, 1, {-0.9f, 0.6f}, {0.1f, 0.1f}, 0};
	Object fragileblock6 = {SOLID, FRAGILE_BLOCK, 0, 1, {-0.7f, 0.3f}, {0.1f, 0.1f}, 0};
	Object fragileblock7 = {SOLID, FRAGILE_BLOCK, 0, 1, {-1.0f, 0.0f}, {0.1f, 0.1f}, 0};
	Object fragileblock8 = {SOLID, FRAGILE_BLOCK, 0, 1, {-0.8f, -0.3f}, {0.1f, 0.1f}, 0};
	Object fragileblock9 = {SOLID, FRAGILE_BLOCK, 0, 1, {-1.0f, -0.6f}, {0.1f, 0.1f}, 0};
	SDL_AddTimer(10, sampleTimerFunc, NULL);

	setPlayerCount(2);

	for(int i = 0; i < number_of_marios; i++)
	{
		mario[i] = getData(getPlayer(i));
	}

	mario[0]->alive = 1;
	mario[0]->pos.x = -0.2f;
	mario[0]->obj_type = MARIO;
	mario[0]->img_index = 0;
	mario[0]->pos.y = ELEVATION + .5f;
	mario[0]->size.x = MARIO_SIZE_X;
	mario[0]->size.y = MARIO_SIZE_Y;
	mario[0]->solid = SOLID;
	mario[0]->hp = 10;
	
	mario[1]->alive = 1;
	mario[1]->obj_type = MARIO;
	mario[1]->img_index = 4;
	mario[1]->pos.x = -0.27f;
	mario[1]->pos.y = ELEVATION + 1.0f;
	mario[1]->size.x = MARIO_SIZE_X;
	mario[1]->size.y = MARIO_SIZE_Y;
	mario[1]->solid = SOLID;
	mario[1]->hp = 10;
	
	for(int i = 0; i < MAX_PLAYERS; i++)
		for(int j = 0; j < MAX_DIGITS; j++)
		{
			pObject pscore = getData(getScoreBoard(i, j));
			pscore->fixed = 1;
			pscore->pos.y = 0.8f;
			pscore->size.y = 0.1f;

			if(j == 0)
			{
				pscore->pos.x = -0.8f + i;
				pscore->size.x = 0.1f;
				pscore->img_index = i * 4;
				pscore->obj_type = MARIO;
			}
			else
			{
				pscore->pos.x = -0.8f + i + 0.05f * (j + 1);
				pscore->size.x = 0.05f;
				pscore->obj_type = DIGIT;
			}
		}

	addObject(newObject(&bckgnd));
	addObject(newObject(&platform4));
	addObject(newObject(&platform3));
	addObject(newObject(&platform2));
	addObject(newObject(&platform1));
	addObject(newObject(&hardblock1));
	addObject(newObject(&hardblock2));
	addObject(newObject(&platform6));
	addObject(newObject(&platform5));
	addObject(newObject(&cloud1));
	addObject(newObject(&cloud2));
	addObject(newObject(&cloud3));
	addObject(newObject(&cloud4));
	addObject(newObject(&cloud5));
	addObject(newObject(&fragileblock1));
	addObject(newObject(&fragileblock2));
	addObject(newObject(&fragileblock3));
	addObject(newObject(&fragileblock4));
	addObject(newObject(&fragileblock5));
	addObject(newObject(&fragileblock6));
	addObject(newObject(&fragileblock7));
	addObject(newObject(&fragileblock8));
	addObject(newObject(&fragileblock9));
	addObject(newObject(&randombox1));
	addObject(newObject(&randombox2));
	for(int i = 0; i < 2; i++)
		for(int j = -1; j < 21; j++)
		{
			ground.pos.y = -1.0f + i * 0.1f;
			ground.pos.x = -1.0f + j * 0.1f;
			addObject(newObject(&ground));
		}

	EventInfo info;

	for(int i = 0; i < getPlayerCount(); i++)
	{
		info.obj1 = getPlayer(i);
		info.event_type = evt_motion_id;
		info.time_interval = 1;
		addEvent(&info, EVT_MAX_PRIORITY);

		info.event_type = evt_animate_id;
		info.time_interval = 6;
		info.obj1 = getPlayer(i);
		addEvent(&info, EVT_NORMAL_PRIORITY);
	}

	info.event_type = evt_check_id;
	info.time_interval = 1;
	addEvent(&info, EVT_MAX_PRIORITY - 1);

	return;
}


static void endProcess(int exit_code)
{
	SDL_Quit();
	exit(exit_code);
	return;
}

//fordebug
#include <iostream>

static void keyboardFunc(Uint8 *key_status, Uint8 *prev_key_status)
{
	int i;
	for (i = 0; i < getPlayerCount(); i++)
	{
		pObject mario = getData(getPlayer(i));
		if(!mario->alive)continue;
		if (key_status[ctrl[i].left])
			//mario->speed.x = -WALK_SPEED_X;
			mario->acc.x += -WALK_ACC;
		if (key_status[ctrl[i].right])
			//mario->speed.x = WALK_SPEED_X;
			mario->acc.x += WALK_ACC;

		if (key_status[ctrl[i].jump])
			if (fabs(mario->acc.y) < PRECISION)
				mario->speed.y = JUMP_SPEED;

		if(key_status[ctrl[i].down] && !prev_key_status[ctrl[i].down])
		{
			static int delay = 0;
			if(fabs(mario->acc.y) < PRECISION)
			{
				mario->pos.y -= ACCEPTABLE_DIST + PRECISION;
				mario->speed.y = -MAX_SPEED_Y / 5;
				delay = 0;
			}
			else
			{
				delay++;

				if(delay == KEYBOARD_ACTION_TIME_DELAY)delay = 0;
				else
					key_status[ctrl[i].down] = 0;
			}
		}

		if (key_status[ctrl[i].quit])
			endProcess(0);

        if (key_status[ctrl[i].shoot] && mario->weapon > 0 && !prev_key_status[ctrl[i].shoot]){
            int direction = mario->img_index % 4 / 2;
			for(int j = 0; j < mario->weapon; j++)
			{
				Object bullet = {SOLID, BULLET, 0, 0, {mario->pos.x + (direction ? -0.05f : 0.1f), mario->pos.y + j * 0.05f}, {0.05f, 0.05f}, i*4, i, 0, 0, {(direction ? -1.0f : 1.0f), 0}};
				hObject hbullet = newObject(&bullet);
				addObject(hbullet);
				EventInfo movebullet = {hbullet, 0, evt_motion_id, 1};
				addEvent(&movebullet, EVT_NORMAL_PRIORITY);
				EventInfo movebulletctrl = {hbullet, 0, evt_automotion_ctrlB_id, 1};
				addEvent(&movebulletctrl, EVT_NORMAL_PRIORITY);
				EventInfo bulletvanish = {hbullet, 0, evt_bulletvanish_id, 300};
				addEvent(&bulletvanish, EVT_NORMAL_PRIORITY);
			}
        }

	}
	return;
}

static void ggInputHandling()
{
    static Uint8 keys[SDLK_LAST], prev_keys[SDLK_LAST];
	SDL_Event event;
	memcpy(prev_keys, keys, SDLK_LAST * sizeof(Uint8));
	while(SDL_PollEvent(&event))
	{
		switch(event.type)
		{
		case SDL_KEYUP:
			keys[event.key.keysym.sym] = 0;
			break;
		case SDL_KEYDOWN:
			keys[event.key.keysym.sym] = 1;

			break;
		case SDL_QUIT:
			endProcess(0);
			break;
		}
	}
	keyboardFunc(keys, prev_keys);
	return;
}

#ifdef _WIN32
int WINAPI WinMain(
  HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,
  int nCmdShow
)
#else
int main(int argc, char *argv[])
#endif
{
	/* initializations here. add when needed */
	ggInit();
	objectInit();
	eventInit();
	defaultHandlerInit();
	crashHandlerInit();
	gameInit();

	/* the main loop */
	while(1)
	{
		/* your main-looped functions here */
		if(flags.timer_flag)
		{
			executeTimerEvents();
			ggDisplay();
			flags.timer_flag = 0;
		}
		ggInputHandling();
	}
	return 0;
}
